package ru.ifmo.network.skype.audio.receive;

import ru.ifmo.network.skype.audio.Config;

import javax.sound.sampled.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author avhaliullin
 */
public class Player implements Runnable {
    private final int bufferSize;
    private final Map<AudioDataReceiver, Float> multipliers = new ConcurrentHashMap<AudioDataReceiver, Float>();
    private SourceDataLine line;
    private final AudioFormat format;
    private final Set<AudioDataReceiver> receivers = new HashSet<AudioDataReceiver>();
    private volatile float volume = 0.5f;
    private final Config config;

    public Player(Config config) throws LineUnavailableException {
        this.bufferSize = config.getBufferSize();
        this.config = config;
        format = config.getAudioFormat();// new AudioFormat(8000, 16, 2, true, false);
        DataLine.Info dlInfo = new DataLine.Info(SourceDataLine.class, format, bufferSize * 2);
        try {
            line = (SourceDataLine) AudioSystem.getLine(dlInfo);
        } catch (LineUnavailableException e) {
            e.printStackTrace();
            System.exit(1);
        }
        line.open(format, bufferSize);
        line.start();
    }

    public void start() {
        new Thread(this).start();
    }

    public void addReceiver(AudioDataReceiver receiver) {
        synchronized (receivers) {
            receivers.add(receiver);
        }
        multipliers.put(receiver, 1f);
    }

    public void removeReceiver(AudioDataReceiver receiver) {
        synchronized (receivers) {
            receivers.remove(receiver);
        }
        multipliers.remove(receiver);
    }

    public void setVolume(AudioDataReceiver receiver, float volume) {
        multipliers.put(receiver, volume);
    }

    public void setVolume(float volume) {
        this.volume = volume;
    }

    public float getVolume() {
        return volume;
    }

    public float getVolume(AudioDataReceiver receiver) {
        if (receiver == null) {
            return 0.5f;
        }
        Float res = multipliers.get(receiver);
        if (res == null) {
            res = 0.5f;
        }
        return res;
    }

    public void run() {
        while (!Thread.interrupted()) {
            byte[] res = new byte[bufferSize];
            ShortBuffer resBuffer = ByteBuffer.wrap(res).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
            synchronized (receivers) {
                for (AudioDataReceiver receiver : receivers) {
                    byte[] buf = receiver.getBuffer();
                    if (buf == null) {
                        continue;
                    }
                    ShortBuffer currentBuffer = ByteBuffer.wrap(buf).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
                    float multiplier = multipliers.get(receiver);
                    for (int i = 0; i < bufferSize / 2; i++) {
                        short v1 = resBuffer.get(i);
                        short v2 = (short) (currentBuffer.get(i) * multiplier * volume);
                        if (((int) v1) + v2 > Short.MAX_VALUE) {
                            resBuffer.put(i, Short.MAX_VALUE);
                        } else {
                            if (((int) v1) + v2 < Short.MIN_VALUE) {
                                resBuffer.put(i, Short.MIN_VALUE);
                            } else {
                                resBuffer.put(i, (short) (v1 + v2));
                            }
                        }
                    }
                }
            }
//            System.out.println("pla:" + Arrays.toString(res));
            line.write(res, 0, bufferSize);
        }
    }

}
